/************************************************************
 *	ProjectName:   LT2911R
 *	FileName:	   LT2911R.c
 *	BuildData:	   2018-08-04
 *	Version：      V1.0
 * 	Author:        xhguo
 * 	Company:	   Lontium
 ************************************************************/

/*
20200603
1. LT2911R_MipiTxDigital: revise error.


*/

#include "dcs.h"
#include "i2c.h"
#include "lt2911.h"
#include "lt2911r_lvds_to_mipi.h"
#include "printf.h"

///#if defined(_lvds_to_mipi_)||defined(_mipi_to_mipi_)||defined(_ttl_to_mipi_)

/****************************************************************
 * Function: 自动配置mipi 的byte clk.
 * 根据Video 的pixel clk 和 mipi lane 数目，算出最小需要的byte clk，
 * 然后加上一些余量, 将小数自动进位，输出byte clk，只用整数byte clk 的jitter 会比较小。
 *****************************************************************/
void LT2911R_MipiTxPllAuto(void)
{
	u16 bitrate = 0;
	u8 seri_div = 0;
	u8 post_div = 2;
	u8 pre_div = 1;
	//u8 loop_div = 0;

	u8 seri_div_reg = 0;
	u8 post_div_reg = 0;
	u8 pre_div_reg = 0;
	u8 loop_div_reg = 0;

	u8 loopx;
	//u8 byteclk = 0;

	bitrate = MIPI_BYTE_CLK / 1000 * 8;
	if (((bitrate > 40) && (bitrate < 80)) || (bitrate == 40)) {
		seri_div = 16;
		seri_div_reg = 3;
	} else if (((bitrate > 80) && (bitrate < 160)) || (bitrate == 80)) {
		seri_div = 8;
		seri_div_reg = 2;
	} else if (((bitrate > 160) && (bitrate < 320)) || (bitrate == 160)) {
		seri_div = 4;
		seri_div_reg = 1;
	} else if (((bitrate > 320) && (bitrate < 640)) || (bitrate == 320)) {
		seri_div = 2;
		seri_div_reg = 0;
	} else if (((bitrate > 640) && (bitrate < 1280)) || (bitrate == 640)) {
		seri_div = 1;
		seri_div_reg = 4;
	}

	if (post_div == 1) {
		post_div_reg = 0;
	} else if (post_div == 2) {
		post_div_reg = 1;
	} else if (post_div == 4) {
		post_div_reg = 2;
	} else if (post_div == 8) {
		post_div_reg = 3;
	}

	if (pre_div == 1) {
		pre_div_reg = 0;
	} else if (pre_div == 2) {
		pre_div_reg = 1;
	} else if (pre_div == 4) {
		pre_div_reg = 2;
	} else if (pre_div == 8) {
		pre_div_reg = 3;
	} else if (pre_div == 16) {
		pre_div_reg = 7;
	}

	loop_div_reg = (u16)(bitrate * seri_div) / post_div / 25;

	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x36, 0x03); // b7:txpll_pd
	HDMI_WriteI2C_Byte(0x37, (0x28 + pre_div_reg));
	HDMI_WriteI2C_Byte(0x38, (seri_div_reg << 4) | (post_div_reg << 2));
	HDMI_WriteI2C_Byte(0x3a, (0x80 + loop_div_reg));
	//    printf("\n%x, ",HDMI_ReadI2C_Byte(0x37));
	//    printf("  %x, ",HDMI_ReadI2C_Byte(0x38));
	//    printf("  %x, ",HDMI_ReadI2C_Byte(0x3a));

	HDMI_WriteI2C_Byte(0xff, 0x87);
	HDMI_WriteI2C_Byte(0x13, 0x00);
	HDMI_WriteI2C_Byte(0x13, 0x80);
	Timer0_Delay1ms(100);
	for (loopx = 0; loopx < 10; loopx++) // Check Tx PLL cal done
	{

		HDMI_WriteI2C_Byte(0xff, 0x87);
		if (HDMI_ReadI2C_Byte(0x1f) & 0x80) {
			if (HDMI_ReadI2C_Byte(0x20) & 0x80) {
				printf("\r\nLT9211 tx pll lock");
			} else {
				printf("\r\nLT9211 tx pll unlocked");
			}
			printf("\r\nLT9211 tx pll cal done");
			break;
		} else {
			printf("\r\nLT9211 tx pll unlocked");
		}
	}
}

/****************************************************************
* Function: Set mipi byte clk by set tx_pll_seri_div and pcr_m.
* pcr_m = (byte_clk_khz*tx_pll_seri_div)*4/25000;

*****************************************************************/
void LT2911R_MipiTxPllManual(void)
{
	// u8 loopx = 0;

	// mipi byte clk refer crystal (25MHz) clock.
	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x36, 0x03); // b7:txpll_pd
	HDMI_WriteI2C_Byte(0x37, 0x88); // 20181221
	HDMI_WriteI2C_Byte(0x38, 0x46); // tx_pll_seri_div = 0

	HDMI_WriteI2C_Byte(0xff, 0x87);
	HDMI_WriteI2C_Byte(0x37, 0x14); // m = 0x14

	HDMI_WriteI2C_Byte(0x13, 0x80); // 20190311
	HDMI_WriteI2C_Byte(0x14, 0x08);
	HDMI_WriteI2C_Byte(0x15, 0x00);

	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x11, 0xfd);
	HDMI_WriteI2C_Byte(0x11, 0xff);

	Timer0_Delay1ms(50);

	HDMI_WriteI2C_Byte(0xff, 0x87);
	if (HDMI_ReadI2C_Byte(0x1f) & 0x80) {
		printf("\r\n LT2911R_MipiTxPllManual: mipi tx pll calibration done");
	} else {
		printf("\r\n LT2911R_MipiTxPllManual: mipi tx pll calibration fail");
	}

	if (HDMI_ReadI2C_Byte(0x20) & 0x80) {
		printf("\r\n LT2911R_MipiTxPllManual: mipi tx pll locked");
	} else {
		printf("\r\n LT2911R_MipiTxPllManual: mipi tx pll unlocked");
	}
}
void LT2911R_MipiTxPhy(void)
{
	HDMI_WriteI2C_Byte(0xff, 0x82);
	HDMI_WriteI2C_Byte(0x62, 0x00); // ttl output disable
	HDMI_WriteI2C_Byte(0x3b, 0x32); // mipi en

	HDMI_WriteI2C_Byte(0xff, 0x81);
	HDMI_WriteI2C_Byte(0x20, 0xfb);
	Timer0_Delay1ms(10);
	HDMI_WriteI2C_Byte(0x20, 0xff);

	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x40, 0x80); // tx_src_sel
	/*port src sel*/
	HDMI_WriteI2C_Byte(0x41, 0x01);
	HDMI_WriteI2C_Byte(0x42, 0x23);
	HDMI_WriteI2C_Byte(0x43, 0x40); // Port A MIPI Lane Swap
	HDMI_WriteI2C_Byte(0x44, 0x12);
	HDMI_WriteI2C_Byte(0x45, 0x34); // Port B MIPI Lane Swap
}

/**************************************************************
Function:
* 根据不同的分辨率配置不同的Timing 参数.

***************************************************************/
void LT2911R_MipiTxDigital(struct video_timing *video_format)
{

#if (MIPI_OUTPUT_PIN_MAP)
	{
#if (MIPI_TX_PORT_B)
		HDMI_WriteI2C_Byte(0xff, 0x86);
		HDMI_WriteI2C_Byte(0x43, 0x23); // 23
		HDMI_WriteI2C_Byte(0x44, 0x41); // 41
		HDMI_WriteI2C_Byte(0x45, 0x02); // 02
#endif

#if (MIPI_TX_PORT_A)
		HDMI_WriteI2C_Byte(0xff, 0x86);
		HDMI_WriteI2C_Byte(0x41, 0x34); // 34
		HDMI_WriteI2C_Byte(0x42, 0x10); // 31
		HDMI_WriteI2C_Byte(0x43, 0x23); // 20
#endif
	}
#endif

	HDMI_WriteI2C_Byte(0xff, 0xd4);
	HDMI_WriteI2C_Byte(0x16, 0x55);
	HDMI_WriteI2C_Byte(0x10, 0x01);
	HDMI_WriteI2C_Byte(0x11, 0x30);
	HDMI_WriteI2C_Byte(0x21, 0x00);

	if (MIPI_BYTE_CLK > 80000) {
		// byte clk: 120000KBps ~80000KBps
		HDMI_WriteI2C_Byte(0xff, 0xd4); // byteclk = 119999, meet spec
		HDMI_WriteI2C_Byte(0x1c, 0x20); // hs_rqst_pre //0x20
		HDMI_WriteI2C_Byte(0x1d, 0x10); // lpx //>0x0a
		HDMI_WriteI2C_Byte(0x1e, 0x0a); // prpr //0x09~0f
		HDMI_WriteI2C_Byte(0x1f, 0x0a); // trail //>0x09

		HDMI_WriteI2C_Byte(0x28, 0x12); // clk_post
		HDMI_WriteI2C_Byte(0x29, 0x0a); // clk_trail ok
		HDMI_WriteI2C_Byte(0x2a, 0x0a); // clk_prepare 0x0e-->0x0a
		HDMI_WriteI2C_Byte(0x2b, 0x20); // clk_zero ok
		HDMI_WriteI2C_Byte(0x2c, 0x08); // clk_pre
	}

	else {
		// byte clk: 50000Kps~80000KBps
		HDMI_WriteI2C_Byte(0xff, 0xd4); // byteclk = 119999, meet spec
		HDMI_WriteI2C_Byte(0x1c, 0x1a); // hs_rqst_pre //0x30
		HDMI_WriteI2C_Byte(0x1d, 0x0a); // lpx //0x0a
		HDMI_WriteI2C_Byte(0x1e, 0x07); // prpr //0x06
		HDMI_WriteI2C_Byte(0x1f, 0x09); // trail //>0x05, <0x09

		HDMI_WriteI2C_Byte(0x28, 0x12); // clk_post
		HDMI_WriteI2C_Byte(0x29, 0x08); // clk_trail
		HDMI_WriteI2C_Byte(0x2a, 0x06); // clk_prepare
		HDMI_WriteI2C_Byte(0x2b, 0x18); // clk_zero
		HDMI_WriteI2C_Byte(0x2c, 0x02); // clk_pre
	}

	HDMI_WriteI2C_Byte(0xff, 0xd4);
	HDMI_WriteI2C_Byte(0x1c, 0x30); // hs_rqst_pre
	HDMI_WriteI2C_Byte(0x1d, 0x0a); // lpx
	HDMI_WriteI2C_Byte(0x1e, 0x06); // prpr
	HDMI_WriteI2C_Byte(0x1f, 0x0a); // trail
	HDMI_WriteI2C_Byte(0x21, 0x00); //[5]byte_swap,[0]burst_clk

	HDMI_WriteI2C_Byte(0x21, 0x02); //[5]byte_swap,[0]burst_clk

	HDMI_WriteI2C_Byte(0x04, 0x05); // hs
	HDMI_WriteI2C_Byte(0x05, 0x05); // hbp
	HDMI_WriteI2C_Byte(0x06, 0x05); // hfp

	HDMI_WriteI2C_Byte(0x07, (u8)(video_format->hact / 256));
	HDMI_WriteI2C_Byte(0x08, (u8)(video_format->hact % 256));

	HDMI_WriteI2C_Byte(0x09, (u8)(video_format->vs % 256));
	HDMI_WriteI2C_Byte(0x0b, (u8)(video_format->vbp % 256));

	HDMI_WriteI2C_Byte(0x0e, (u8)(video_format->vfp / 256));
	HDMI_WriteI2C_Byte(0x0f, (u8)(video_format->vfp % 256));

	HDMI_WriteI2C_Byte(0x0c, (u8)(video_format->vact / 256));
	HDMI_WriteI2C_Byte(0x0d, (u8)(video_format->vact % 256));
}

void LT2911R_Dsi_Out_Enable(bool on)
{
	// static bool flag_lt2911r_dsi_out = 0;
	if (on) {
		HDMI_WriteI2C_Byte(0xff, 0x81); // 20190311
		HDMI_WriteI2C_Byte(0x0e, 0x00);
		HDMI_WriteI2C_Byte(0x0e, 0xff); // mipi Tx reset

		HDMI_WriteI2C_Byte(0xff, 0xd4);
		HDMI_WriteI2C_Byte(0x13, (MIPI_LANE_CNT << 4) | MIPI_HBLANK_MODE);
	} else {
		HDMI_WriteI2C_Byte(0xff, 0xd4);
		HDMI_WriteI2C_Byte(0x13, 0x00);
	}
}
void Panel_Display(bool on)
{
	static bool flag_panel_display_on = 0;
	if (on) {
		if (!flag_panel_display_on) {
			LCD_Reset();
			Dcs_Display_On(1);
			LT2911R_Dsi_Out_Enable(1);
			flag_panel_display_on = 1;
			printf("\r\n\33[35m Panel_Display: on\033[37m");
		}
	} else {
		if (flag_panel_display_on) {
			LT2911R_Dsi_Out_Enable(0);
			Dcs_Display_On(0);
			flag_panel_display_on = 0;
			printf("\r\n\33[35m Panel_Display: off\033[37m");
		}
	}
}

void LT2911R_MipiTxClkCheck_Debug(void)
{
	u32 mipi_byte_clk;
	HDMI_WriteI2C_Byte(0xff, 0x86);
	HDMI_WriteI2C_Byte(0x10, 0x11);
	Timer0_Delay1ms(100);
	mipi_byte_clk = HDMI_ReadI2C_Byte(0x18);

	if ((mipi_byte_clk & 0xf0) == 0x60) {
		mipi_byte_clk &= 0x0f;
		mipi_byte_clk = (mipi_byte_clk << 8) + HDMI_ReadI2C_Byte(0x19);
		mipi_byte_clk = (mipi_byte_clk << 8) + HDMI_ReadI2C_Byte(0x1a);
		printf("\r\n LT2911R_MipiTxClkCheck_Debug: %d", mipi_byte_clk);
		// printdec_u32(mipi_byte_clk);
	} else
		printf("\r\n LT2911R_MipiTxClkCheck_Debug: unstable");
}

//#endif
